<?php

namespace Dreamcat\Class2Array\Impl;

use DreamCat\Array2Class\Array2ClassConverter;
use DreamCat\Array2Class\Array2ClassInterface;
use DreamCat\Array2Class\Exception\ConvertFailed;
use Dreamcat\Class2Array\Enum\JsonEncodeOption;
use Dreamcat\Class2Array\Exception\EncodeError;
use Dreamcat\Class2Array\JsonEncode;
use Dreamcat\Class2Array\JsonValueFixer;

/**
 * 支持将未实现JsonSerializable接口的对象转为json字符串的编码器
 * @author vijay
 */
class Class2Json implements JsonEncode
{
    /** @var int json序列化的参数 */
    private int $jsonOption = 0;

    /** @var int json编码最大深度 */
    private int $jsonDepth = 512;

    /** @var JsonValueFixer json数据修正器 */
    private JsonValueFixer $fixer;

    /** @var Array2ClassInterface 数组转类接口 */
    private Array2ClassInterface $array2Class;

    /**
     * 将数据编码为json
     * @param mixed $data 要编码的数据
     * @return string 编后的结果
     */
    public function jsonEncode(mixed $data): string
    {
        return $this->jsonEncodeWithOption($this->getFixer()->fixValue($data));
    }

    /**
     * @inheritDoc
     */
    public function jsonDecode(string $data, string $type): mixed
    {
        $aryData = json_decode($data, true);
        $error = json_last_error();
        if ($error) {
            throw new EncodeError(json_last_error_msg(), $error);
        }
        try {
            return $this->getArray2Class()->convert($aryData, $type);
        } catch (ConvertFailed $convertFailed) {
            throw new EncodeError($convertFailed->getMessage(), $convertFailed->getCode(), $convertFailed);
        }
    }

    /**
     * json编码
     * @param mixed $data 要编码的数据，要求非对象或已实现JsonSerializable接口
     * @return string 编码后字符串
     */
    protected function jsonEncodeWithOption(mixed $data): string
    {
        $result = json_encode($data, $this->getJsonOption(), $this->getJsonDepth());
        $error = json_last_error();
        if ($error) {
            throw new EncodeError(json_last_error_msg(), $error);
        }
        return $result;
    }

    /**
     * @return JsonValueFixer json数据修正器
     */
    public function getFixer(): JsonValueFixer
    {
        if (!isset($this->fixer)) {
            $this->fixer = new DefaultJsonValueFixer();
        }
        return $this->fixer;
    }

    /**
     * @param JsonValueFixer $fixer json数据修正器
     */
    public function setFixer(JsonValueFixer $fixer): void
    {
        $this->fixer = $fixer;
    }

    /**
     * @return Array2ClassInterface 数组转类接口
     */
    public function getArray2Class(): Array2ClassInterface
    {
        if (!isset($this->array2Class)) {
            $this->array2Class = new Array2ClassConverter();
        }
        return $this->array2Class;
    }

    /**
     * @param Array2ClassInterface $array2Class 数组转类接口
     * @codeCoverageIgnore
     */
    public function setArray2Class(Array2ClassInterface $array2Class): void
    {
        $this->array2Class = $array2Class;
    }

    /**
     * 启用某个选项
     * @param JsonEncodeOption $jsonEncodeOption 编码选项
     */
    public function enableOption(JsonEncodeOption $jsonEncodeOption): void
    {
        $this->setJsonOption($this->getJsonOption() | $jsonEncodeOption->getValue());
    }

    /**
     * 启用某些选项
     * @param JsonEncodeOption[] $jsonEncodeOptions 编码选项列表
     */
    public function enableOptions(array $jsonEncodeOptions): void
    {
        $this->setJsonOption($this->getJsonOption() | JsonEncodeOption::sumOptions($jsonEncodeOptions));
    }

    /**
     * 禁用某个选项
     * @param JsonEncodeOption $jsonEncodeOption 编码选项
     */
    public function disableOption(JsonEncodeOption $jsonEncodeOption): void
    {
        $this->setJsonOption($this->getJsonOption() & ~$jsonEncodeOption->getValue());
    }

    /**
     * 禁用某些选项
     * @param JsonEncodeOption[] $jsonEncodeOptions 编码选项列表
     */
    public function disableOptions(array $jsonEncodeOptions): void
    {
        $this->setJsonOption($this->getJsonOption() & ~JsonEncodeOption::sumOptions($jsonEncodeOptions));
    }

    /**
     * @return int json编码最大深度
     */
    public function getJsonDepth(): int
    {
        return $this->jsonDepth;
    }

    /**
     * @param int $jsonDepth json编码最大深度
     */
    public function setJsonDepth(int $jsonDepth): void
    {
        $this->jsonDepth = $jsonDepth;
    }

    /**
     * @param int $jsonOption
     */
    public function setJsonOption(int $jsonOption): void
    {
        $this->jsonOption = $jsonOption;
    }

    /**
     * @return int json序列化的参数
     */
    public function getJsonOption(): int
    {
        return $this->jsonOption;
    }
}

# end of file
